﻿using System;
using System.Drawing;
using System.IO;
using System.Windows.Forms;

namespace files_merge
{
    /// <summary>
    /// 主窗体
    /// </summary>
    public partial class MainForm : Form
    {
        /// <summary>
        /// 构造方法
        /// </summary>
        public MainForm()
        {
            // 初始化各组件
            InitializeComponent();
            // 禁用放缩
            MaximumSize = MinimumSize = Size;
            // 取消最大化框
            MaximizeBox = false;
            // 窗口位于屏幕中心
            StartPosition = FormStartPosition.Manual;
            int work_area_w = SystemInformation.WorkingArea.Width;
            int work_area_h = SystemInformation.WorkingArea.Height;
            Location = new Point((work_area_w - Size.Width) / 2, (work_area_h - Size.Height) / 2);
            // 清除列表
            gridFileInfo.Rows.Clear();
            // 合并按键和删除按键无效
            menuDeleteFile.Enabled = false;
            menuClearFile.Enabled = false;
            menuMergeFile.Enabled = false;
            menuFilesInfo.Enabled = false;
            // 默认HEX显示
            chkHexFormat.Checked = true;
            gridFileInfo.Columns[0].DefaultCellStyle.Format = "X";
            gridFileInfo.Columns[1].DefaultCellStyle.Format = "X";
            gridFileInfo.Columns[2].DefaultCellStyle.Format = "X";
            // 将默认字节放到菜单中
            menuDefaultPadByte.Text = "空位填充字节";
            // 填充下拉框
            menuPadByteCombox.Items.Clear();
            for (int i = 0; i <= byte.MaxValue; i++)
            {
                menuPadByteCombox.Items.Add(i.ToString("X2"));
            }
            menuPadByteCombox.SelectedIndex = 0xFF;
        }

        /// <summary>
        /// 数据行转信息结构
        /// </summary>
        /// <param name="row">数据行</param>
        /// <returns>信息结构</returns>
        private RowDataInfo DataGridViewRowToInfo(DataGridViewRow row)
        {
            RowDataInfo info = new RowDataInfo();

            info.StartOffsetAddr = Convert.ToUInt32(row.Cells[0].Value);
            info.FileLength = Convert.ToUInt32(row.Cells[1].Value);
            info.EndOffsetAddr = Convert.ToUInt32(row.Cells[2].Value);
            info.FileName = row.Cells[3].Value.ToString();
            info.FileComment = row.Cells[4].Value.ToString();

            return info;
        }

        /// <summary>
        /// 信息结构转数据行
        /// </summary>
        /// <param name="info">信息结构</param>
        /// <returns>数据行</returns>
        private DataGridViewRow InfoToDataGridViewRow(RowDataInfo info)
        {
            DataGridViewRow row = new DataGridViewRow();

            row.CreateCells(gridFileInfo);
            row.Cells[0].Value = info.StartOffsetAddr;
            row.Cells[1].Value = info.FileLength;
            row.Cells[2].Value = info.EndOffsetAddr;
            row.Cells[3].Value = info.FileName;
            row.Cells[4].Value = info.FileComment;

            return row;
        }

        /// <summary>
        /// 获取下一个偏移地址
        /// </summary>
        /// <returns></returns>
        private uint GetNextOffsetAddr()
        {
            if (gridFileInfo.Rows.Count == 0)
            {
                return 0;
            }
            else
            {
                RowDataInfo info = DataGridViewRowToInfo(gridFileInfo.Rows[gridFileInfo.Rows.Count - 1]);
                return info.EndOffsetAddr + 1;
            }
        }

        /// <summary>
        /// “添加文件”按钮点击事件
        /// </summary>
        /// <param name="sender">发送事件的控件</param>
        /// <param name="e">事件参数</param>
        private void menuAddFile_Click(object sender, EventArgs e)
        {
            // 创建工作区域
            Rectangle rect = new Rectangle(Location, Size);
            // 创建输入参数
            AddFileFormInArgs in_args;
            in_args.OffsetAddr = GetNextOffsetAddr();
            // 创建窗体
            AddFileForm form = new AddFileForm(rect, in_args);
            // 以对话框的形式显示，否则无法向外部传递参数
            if (form.ShowDialog() == DialogResult.OK)
            {
                RowDataInfo info = new RowDataInfo();
                info.StartOffsetAddr = form.OutArgs.OffsetAddr;
                info.FileLength = form.OutArgs.FileLength;
                info.EndOffsetAddr = info.StartOffsetAddr + info.FileLength - 1;
                info.FileName = form.OutArgs.FileName;
                info.FileComment = form.OutArgs.FileComment;
                DataGridViewRow row = InfoToDataGridViewRow(info);
                // 添加行
                gridFileInfo.Rows.Add(row);
                // 升序排列
                gridFileInfo.Sort(gridFileInfo.Columns[0], System.ComponentModel.ListSortDirection.Ascending);
                // 不选中任何行
                gridFileInfo.ClearSelection();
                // 显示删除所有文件按键
                if (menuClearFile.Enabled == false)
                {
                    menuClearFile.Enabled = true;
                }
                /* 显示合并按键 */
                if (menuMergeFile.Enabled == false)
                {
                    menuMergeFile.Enabled = true;
                    menuFilesInfo.Enabled = true;
                }
            }
        }

        /// <summary>
        /// 删除选中的文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void menuDeleteFile_Click(object sender, EventArgs e)
        {
            /* 判断是否有项可以删除 */
            if (gridFileInfo.SelectedRows.Count == 0)
            {
                MessageBox.Show("未选择要删除的行，无法删除，请选择待删除行后再试", "温馨提示");
                return;
            }

            /* 遍历删除选中行 */
            foreach (DataGridViewRow row in gridFileInfo.SelectedRows)
            {
                gridFileInfo.Rows.Remove(row);
            }

            if (gridFileInfo.Rows.Count > 0)
            {
                menuMergeFile.Enabled = false;
                menuFilesInfo.Enabled = false;
            }
        }

        /// <summary>
        /// 是否HEX显示选择处理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void chkHexFormat_CheckedChanged(object sender, EventArgs e)
        {
            if (chkHexFormat.Checked)
            {
                gridFileInfo.Columns[0].DefaultCellStyle.Format = "X";
                gridFileInfo.Columns[1].DefaultCellStyle.Format = "X";
                gridFileInfo.Columns[2].DefaultCellStyle.Format = "X";
            }
            else
            {
                gridFileInfo.Columns[0].DefaultCellStyle.Format = "D1";
                gridFileInfo.Columns[1].DefaultCellStyle.Format = "D1";
                gridFileInfo.Columns[2].DefaultCellStyle.Format = "D1";
            }
        }

        /// <summary>
        /// 删除全部文件按键处理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void menuDeleteAll_Click(object sender, EventArgs e)
        {
            // 删除全部行
            gridFileInfo.Rows.Clear();
            menuMergeFile.Enabled = false;
            menuFilesInfo.Enabled = false;
        }

        /// <summary>
        /// 列表选择发生改变时处理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void gridFileInfo_SelectionChanged(object sender, EventArgs e)
        {
            if (gridFileInfo.SelectedRows.Count > 0)
            {
                menuDeleteFile.Enabled = true;
            }
            else
            {
                menuDeleteFile.Enabled = false;
            }
        }

        /// <summary>
        /// 行发生改变
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void gridFileInfo_RowsChanged(object sender, EventArgs e)
        {
            if (gridFileInfo.Rows.Count > 0)
            {
                menuClearFile.Enabled = true;
            }
            else
            {
                menuClearFile.Enabled = false;
            }
        }

        /// <summary>
        /// 添加多个文件按钮点击处理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void menuAddFiles_Click(object sender, EventArgs e)
        {
            // 创建工作区域
            Rectangle rect = new Rectangle(Location, Size);
            // 创建输入参数
            AddFilesFormInArgs in_args;
            in_args.OffsetAddr = GetNextOffsetAddr();
            // 创建窗体
            AddFilesForm form = new AddFilesForm(rect, in_args);
            // 以对话框的形式显示，否则无法向外部传递参数
            if (form.ShowDialog() == DialogResult.OK)
            {
                RowDataInfo info = new RowDataInfo();
                uint offset_addr = form.OutArgs.OffsetAddr;
                for (int i = 0; i < form.OutArgs.FileNum; i++)
                {
                    info.StartOffsetAddr = offset_addr;
                    info.FileLength = form.OutArgs.FileLength[i];
                    info.EndOffsetAddr = offset_addr + info.FileLength - 1;
                    info.FileName = form.OutArgs.FileName[i];
                    info.FileComment = "";
                    DataGridViewRow row = InfoToDataGridViewRow(info);
                    // 添加行
                    gridFileInfo.Rows.Add(row);
                    offset_addr += info.FileLength;
                }

                // 升序排列
                gridFileInfo.Sort(gridFileInfo.Columns[0], System.ComponentModel.ListSortDirection.Ascending);

                // 不选中任何行
                gridFileInfo.ClearSelection();
                // 显示删除所有文件按键
                if (menuClearFile.Enabled == false)
                {
                    menuClearFile.Enabled = true;
                }
                /* 显示合并按键 */
                if (menuMergeFile.Enabled == false)
                {
                    menuMergeFile.Enabled = true;
                    menuFilesInfo.Enabled = true;
                }
            }
        }

        /// <summary>
        /// 合并文件按钮点击处理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void menuMerge_Click(object sender, EventArgs e)
        {
            /* 判断是否列表为空 */
            if (gridFileInfo.Rows.Count <= 0)
            {
                MessageBox.Show("无文件进行合并，请添加要合并的文件", "温馨提示", MessageBoxButtons.OK);
                return;
            }

            /* 遍历文件，查询是否有文件地址重叠 */
            RowDataInfo info_last = DataGridViewRowToInfo(gridFileInfo.Rows[0]);
            for (int i = 1; i < gridFileInfo.Rows.Count; i++)
            {
                RowDataInfo info_now = DataGridViewRowToInfo(gridFileInfo.Rows[i]);
                if (info_now.StartOffsetAddr <= info_last.EndOffsetAddr)
                {
                    /* 提示长度超标 */
                    MessageBox.Show("文件数据有重叠\r\n" + info_last.FileName + "\r\n" + info_now.FileName, "温馨提示", MessageBoxButtons.OK);
                    return;
                }
                info_last = info_now;
            }

            /* 结束地址的最大值作为文件长度 */
            ulong file_size = info_last.EndOffsetAddr + 1;

            try
            {
                /* 文件缓存 */
                byte[] buf = new byte[file_size];
                /* 填充默认值 */
                for (ulong i = 0; i < file_size; i++)
                {
                    buf[i] = (byte)menuPadByteCombox.SelectedIndex;
                }
                /* 遍历文件 */
                for (int i = 0; i < gridFileInfo.Rows.Count; i++)
                {
                    RowDataInfo info = DataGridViewRowToInfo(gridFileInfo.Rows[i]);
                    byte[] data = File.ReadAllBytes(info.FileName);
                    Array.Copy(data, 0, buf, info.StartOffsetAddr, info.FileLength);
                }
                /* 保存文件 */
                SaveFileDialog sfd = new SaveFileDialog();
                sfd.Filter = "二进制文件(*.bin) | *.bin";
                sfd.Title = "保存合并后的文件";
                if (sfd.ShowDialog() == DialogResult.OK)
                {
                    File.WriteAllBytes(sfd.FileName, buf);
                }
            }
            catch (Exception ex)
            {
                /* 提示数据异常 */
                MessageBox.Show("合并文件大小异常\r\n" + ex.Message, "温馨提示", MessageBoxButtons.OK);
            }
        }

        /// <summary>
        /// 保存为配置文件菜单处理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void menuSaveCfgFile_Click(object sender, EventArgs e)
        {
            /* 判断是否列表为空 */
            if (gridFileInfo.Rows.Count <= 0)
            {
                MessageBox.Show("列表内无文件，请添加文件", "温馨提示", MessageBoxButtons.OK);
                return;
            }

            /* 保存文件 */
            SaveFileDialog sfd = new SaveFileDialog();
            sfd.Filter = "文件合并配置文件(*.fmerge) | *.fmerge";
            sfd.Title = "保存配置文件";
            if (sfd.ShowDialog() == DialogResult.OK)
            {
                ConfigFile file = new ConfigFile();
                RowDataInfo[] data_rows = new RowDataInfo[gridFileInfo.Rows.Count];
                int count = 0;
                foreach (DataGridViewRow row in gridFileInfo.Rows)
                {
                    data_rows[count++] = DataGridViewRowToInfo(row);
                }
                file.Create(sfd.FileName, (byte)menuPadByteCombox.SelectedIndex, data_rows);
                MessageBox.Show("导出配置文件成功", "成功提示", MessageBoxButtons.OK);
            }
        }

        /// <summary>
        /// 导入配置文件菜单处理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void menuOpenCfgFile_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Filter = "文件合并配置文件(*.fmerge) | *.fmerge";
            ofd.Title = "读取配置文件";
            if (ofd.ShowDialog() == DialogResult.OK)
            {
                ConfigFile cfg_file = new ConfigFile();
                ConfigFileArgs args = cfg_file.Read(ofd.FileName);

                menuPadByteCombox.SelectedIndex = args.padByte;

                gridFileInfo.Rows.Clear();
                foreach (RowDataInfo info in args.rowsData)
                {
                    DataGridViewRow row = InfoToDataGridViewRow(info);
                    gridFileInfo.Rows.Add(row);
                }
                // 升序排列
                gridFileInfo.Sort(gridFileInfo.Columns[0], System.ComponentModel.ListSortDirection.Ascending);

                // 不选中任何行
                gridFileInfo.ClearSelection();
                // 显示删除所有文件按键
                if (menuClearFile.Enabled == false)
                {
                    menuClearFile.Enabled = true;
                }
                /* 显示合并按键 */
                if (menuMergeFile.Enabled == false)
                {
                    menuMergeFile.Enabled = true;
                    menuFilesInfo.Enabled = true;
                }
            }
        }

        /// <summary>
        /// 生成文件信息列表，用于用户程序控制
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void menuFilesInfo_Click(object sender, EventArgs e)
        {
            SaveFileDialog sfd = new SaveFileDialog();
            sfd.Title = "保存文件数据存放信息";
            sfd.Filter = "文本文件(*.txt)|*.txt";
            sfd.FileName = DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss") + ".txt";
            if (sfd.ShowDialog() == DialogResult.OK)
            {
                StreamWriter sw = new StreamWriter(new FileStream(sfd.FileName, FileMode.Create));
                foreach (DataGridViewRow row in gridFileInfo.Rows)
                {
                    RowDataInfo info = DataGridViewRowToInfo(row);
                    sw.WriteLine("0x" + info.StartOffsetAddr.ToString("X8") +
                                 ", " +
                                 "0x" + info.FileLength.ToString("X8") +
                                 ", " +
                                 "/* " +
                                 Path.GetFileNameWithoutExtension(info.FileName) +
                                 " */");
                }
                sw.Flush();
                sw.Close();
                MessageBox.Show("生成成功！");
            }
        }
    }

    /// <summary>
    /// 行数据信息
    /// </summary>
    public struct RowDataInfo
    {
        // 起始偏移地址
        public uint StartOffsetAddr;
        // 文件长度
        public uint FileLength;
        // 结束偏移地址
        public uint EndOffsetAddr;
        // 文件路径名称
        public string FileName;
        // 文件注释
        public string FileComment;
    }
}
